/********************************************************/
/*	Copyright (C) 2016 Gong Li Bin		 	*/
/*	Project:	GlbLib-1.0.0			*/
/*	Author:		gong_libin			*/
/*	Date:		2016_06_01			*/
/*	File:		GlbList.cpp			*/
/********************************************************/

#include "GlbList.h"

namespace GlbCls
{

CGlbList::CGlbList()
{
	m_ulCount = 0;
	m_pstHead = NULL;
	m_pstTail = NULL;

	m_pFunDup = NULL;
	m_pFunFree = NULL;
	m_pFunMatch = NULL;
}

CGlbList::~CGlbList()
{
}

void CGlbList::GlbListRelease()
{
	GLBNODE_S* pstNext = NULL;
	GLBNODE_S* pstCur = m_pstHead;

	while(m_ulCount --) {
		pstNext = pstCur->m_pstNext;
		if (NULL != m_pFunFree) {
			(*m_pFunFree)(pstCur->m_pValue);
		}
		GlbFree((void**)&pstCur);
		pstCur = pstNext;
	}

	return;
}

int CGlbList::GlbListAddHead(void* pValue)
{
	GLBNODE_S* pstNode = NULL;

	if (NULL != (pstNode = (GLBNODE_S*)GlbMalloc(sizeof(GLBNODE_S)))) {
		pstNode->m_pValue = pValue;
		if (m_ulCount > 0) {
			pstNode->m_pstPrev = NULL;
			pstNode->m_pstNext = m_pstHead;
			m_pstHead->m_pstPrev = pstNode;
			m_pstHead = pstNode;
		}
		else {
			m_pstHead = m_pstTail = pstNode;
			pstNode->m_pstPrev = pstNode->m_pstNext = NULL;
		}
		m_ulCount += 1;
	}
	else {
		GLB_ERROR("%s\n", strerror(errno));
		return GLB_FAILURE;
	}

	return GLB_SUCCESS;
}

int CGlbList::GlbListAddTail(void* pValue)
{
	GLBNODE_S* pstNode = NULL;

	if (NULL != (pstNode = (GLBNODE_S*)GlbMalloc(sizeof(GLBNODE_S)))) {
		pstNode->m_pValue = pValue;
		if (m_ulCount > 0) {
			pstNode->m_pstPrev = m_pstTail;
			pstNode->m_pstNext = NULL;
			m_pstTail->m_pstNext = pstNode;
			m_pstTail = pstNode;
		}
		else {
			m_pstHead = m_pstTail = pstNode;
			pstNode->m_pstPrev = pstNode->m_pstNext = NULL;
		}
		m_ulCount += 1;
	}
	else {
		GLB_ERROR("%s\n", strerror(errno));
		return GLB_FAILURE;
	}

	return GLB_SUCCESS;
}
 
int CGlbList::GlbListInsert(GLBNODE_S* pstBase, void* pValue, int iDir)
{
	GLBNODE_S* pstNode = NULL;

	if (NULL != (pstNode = (GLBNODE_S*)GlbMalloc(sizeof(GLBNODE_S)))) {
		pstNode->m_pValue = pValue;
		switch (iDir) {
			case GLB_HEAD:
				pstNode->m_pstNext = pstBase;
				pstNode->m_pstPrev = pstBase->m_pstPrev;
				if (m_pstHead == pstBase) {
					m_pstHead = pstNode;
				}
				break;
			case GLB_TAIL:
				pstNode->m_pstPrev = pstBase;
				pstNode->m_pstNext = pstBase->m_pstNext;
				if (m_pstTail == pstBase) {
					m_pstTail = pstNode;
				}
				break;
			default:
				break;
		}
		if (NULL != pstNode->m_pstPrev) {
			pstNode->m_pstPrev->m_pstNext = pstNode;
		}
		if (NULL != pstNode->m_pstNext) {
			pstNode->m_pstNext->m_pstPrev = pstNode;
		}
		m_ulCount += 1;
	}
	else {
		GLB_ERROR("%s\n", strerror(errno));
		return GLB_FAILURE;
	}

	return GLB_SUCCESS;
}

void CGlbList::GlbListDelete(GLBNODE_S* pstNode)
{
	if (NULL != pstNode->m_pstPrev) {
		pstNode->m_pstPrev->m_pstNext = pstNode->m_pstNext;
	}
	else {
		m_pstHead = pstNode->m_pstNext;
	}

	if (NULL != pstNode->m_pstNext) {
		pstNode->m_pstNext->m_pstPrev = pstNode->m_pstPrev;
	}
	else {
		m_pstTail = pstNode->m_pstPrev;
	}

	if (NULL != m_pFunFree) {
		(*m_pFunFree)(pstNode->m_pValue);
	}

	GlbFree((void**)&pstNode);
	m_ulCount --;

	return;
}

void CGlbList::GlbListIterator(int iDir)
{
	switch (iDir) {
		case GLB_HEAD:
			m_stIter.m_pstNext = m_pstHead;
			break;
		case GLB_TAIL:
			m_stIter.m_pstNext = m_pstTail;
			break;
		default:
			break;
	}

	m_stIter.m_iDirection = iDir;

	return;
}

GLBNODE_S* CGlbList::GlbListNext()
{
	GLBNODE_S* pstCur = NULL;

	if (NULL != (pstCur = m_stIter.m_pstNext)) {
		switch (m_stIter.m_iDirection) {
			case GLB_HEAD:
				m_stIter.m_pstNext = pstCur->m_pstNext;
				break;
			case GLB_TAIL:
				m_stIter.m_pstNext = pstCur->m_pstPrev;
				break;
			default:
				break;
		}
	}

	return pstCur;
}

int CGlbList::GlbListDup(CGlbList& rCList)
{
	void* pValue = NULL;
	int iReturn = GLB_SUCCESS;
	GLBNODE_S* pstNode = NULL;

	m_pFunDup = rCList.GlbListGetDup();
	m_pFunFree = rCList.GlbListGetFree();
	m_pFunMatch = rCList.GlbListGetMatch();

	rCList.GlbListRewindHead();
	while (NULL != (pstNode = rCList.GlbListNext())) {
		if (NULL != m_pFunDup) {
			if (NULL == (pValue = (*m_pFunDup)(pstNode->m_pValue))) {
				iReturn = GLB_FAILURE;
				GlbListRelease();
				break;
			}
		}
		else {
			pValue = pstNode->m_pValue;
		}

		if (GLB_SUCCESS != GlbListAddTail(pValue)) {
			iReturn = GLB_FAILURE;
			GlbListRelease();
			break;
		}
	}

	return iReturn;
}

GLBNODE_S* CGlbList::GlbListSearch(void* pKey)
{
	GLBNODE_S* pstNode = NULL;

	GlbListRewindHead();
	while (NULL != (pstNode = GlbListNext())) {
		if (NULL != m_pFunMatch) {
			if (GLB_SUCCESS == (*m_pFunMatch)(pKey, pstNode->m_pValue)) {
				break;
			}
		}
		else {
			if (pKey != pstNode->m_pValue) {
				pstNode = NULL;
			}
		}
	}

	return pstNode;
}

GLBNODE_S* CGlbList::GlbListIndex(long lIndex)
{
	GLBNODE_S* pstNode = NULL;

	if (lIndex < 0) {
		pstNode = m_pstTail;
		lIndex = (- lIndex) - 1;
		while (lIndex -- && NULL != pstNode) pstNode = pstNode->m_pstPrev;
	}
	else {
		pstNode = m_pstHead;
		while (lIndex -- && NULL != pstNode) pstNode = pstNode->m_pstNext;
	}

	return pstNode;
}

void CGlbList::GlbListRewindHead()
{
	m_stIter.m_pstNext = m_pstHead;
	m_stIter.m_iDirection = GLB_HEAD;

	return;
}

void CGlbList::GlbListRewindTail()
{
	m_stIter.m_pstNext = m_pstTail;
	m_stIter.m_iDirection = GLB_TAIL;

	return;
}

void CGlbList::GlbListRotate()
{
	GLBNODE_S* pstNode = m_pstTail;

	if (GlbListGetCount() > 1) {
		m_pstTail = pstNode->m_pstPrev;
		m_pstTail->m_pstNext = NULL;
		m_pstHead->m_pstPrev = pstNode;
		pstNode->m_pstPrev = NULL;
		pstNode->m_pstNext = m_pstHead;
		m_pstHead = pstNode;
	}

	return;
}

} /* GlbCls */
